package com.peking.donations.plugins.pay.wx.web.controller;

import com.peking.donations.base.util.Signature;
import com.peking.donations.base.util.XMLUtil;
import com.peking.donations.plugins.pay.service.NotifyCallBackService;
import com.peking.donations.plugins.pay.wx.constants.CacheNames;
import com.peking.donations.plugins.pay.wx.utils.Configure;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

/**
 * Created by YGR on 16/3/4.
 */
@Controller
@RequestMapping("/third/wx/pay")
public class PayNotifyController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    Configure configure;

    @Autowired
    CacheManager cacheManager;

    @Autowired
    NotifyCallBackService callBackService;

    @RequestMapping(value = "/notify", method = RequestMethod.POST)
    public void notify(HttpServletRequest req, HttpServletResponse res) throws IOException {
        String fail_rc = "<xml><return_code>FAIL</return_code><return_msg>%s</return_msg></xml>";
        String success_rc = "<xml><return_code>SUCCESS</return_code></xml>";

        StringBuffer retData = new StringBuffer();
        String line;
        try {
            BufferedReader reader = req.getReader();
            while ((line = reader.readLine()) != null) {
                retData.append(line);
            }
        } catch (Exception e) {
            logger.error("接收微信回调数据出现异常。", e);
            res.getWriter().print(String.format(fail_rc, "接受数据失败"));
            res.flushBuffer();
            return;
        }

        logger.debug("微信回调数据：{}", retData.toString());

        Map<String, String> wxRetMap = XMLUtil.fromXmlString(retData.toString());

        // 按照官方文档，需要按照result_code而不是return_code来区分最终结果是成功还是失败
        if (!"SUCCESS".equals(wxRetMap.get("result_code"))) {
            logger.error("支付不成功！微信返回信息为：{}", wxRetMap);
            res.getWriter().print(success_rc);
            res.flushBuffer();
            return;
        }

        // 返回结果后，还需要验证sign是否正确
        String sign = (String) wxRetMap.remove("sign");
        String appid = (String) wxRetMap.get("appid");
        try {
            if (!sign.equals(Signature.getSign(wxRetMap, configure.getKey()))) {
                logger.error("微信回调数据的签名不正确。");
                res.getWriter().print(String.format(fail_rc, "签名失败"));
                res.flushBuffer();
                return;
            }
        } catch (Exception e) {
            logger.error("微信回调数据的签名不正确。");
            res.getWriter().print(String.format(fail_rc, "签名失败"));
            res.flushBuffer();
            return;
        }

        // 验证重复回调
        String outTradeNo = (String) wxRetMap.get("out_trade_no");
        if (this.existPayFlag(outTradeNo)) {
            // redis中已经存在，说明是重复的回调通知，忽略
            logger.warn("重复的微信回调通知，已忽略，tid={}", outTradeNo);
            res.getWriter().print(success_rc);
            res.flushBuffer();
            return;
        } else {
            this.setPayFlag(outTradeNo);
        }

        // 处理订单
        try {
            int amount =  Integer.parseInt(wxRetMap.get("total_fee"));
            boolean checkOrder = callBackService.checkOrder(outTradeNo, amount);
            if (!checkOrder) {
                logger.warn("错误的订单信息，tid={}", outTradeNo);
                res.getWriter().print(String.format(fail_rc, "错误的订单信息"));
                res.flushBuffer();
                return;
            }
            String fromTid = (String) wxRetMap.get("transaction_id");// 微信支付的transactionId
            String trade_type = (String) wxRetMap.get("trade_type");
            callBackService.updateOrderStateSuccess(outTradeNo, fromTid);
        } catch (Exception e) {
            logger.error("支付回调过程中出现异常。", e);
            this.setPayFailedFlag(outTradeNo);// payTrans不清空，等待自动失效

            res.getWriter().print(String.format(fail_rc, e.getMessage()));
            res.flushBuffer();
            return;
        }

        res.getWriter().print(success_rc);
        res.flushBuffer();
    }

    private void setPayFlag(String key) {
        Cache cache = cacheManager.getCache(CacheNames.PAY_SUCCESS_TRANSACTIONS.name());
        cache.put(key, "1");
    }

    private boolean existPayFlag(String key) {
        Cache cache = cacheManager.getCache(CacheNames.PAY_SUCCESS_TRANSACTIONS.name());
        return cache.get(key) != null ? true : false;
    }

    private void setPayFailedFlag(String key) {
        Cache cache = cacheManager.getCache(CacheNames.PAY_FAILED_TRANSACTIONS.name());
        cache.put(key, DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
    }

}
